#pragma once

#include <sys/epoll.h>
#include <signal.h>
#include <fcntl.h>
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ThreadPool.hpp"

const int PORT = 8080;
const int TRIGGER_EVENTS_BUFFER_SIZE = 1024; // 接受触发事件缓冲区大小

class HttpServer
{
public:
    HttpServer(int port = PORT)
        : _port(port)
    {
    }
    ~HttpServer()
    {
        if (_lfd > 0)
            close(_lfd);
        if (_epfd > 0)
            close(_epfd);
    }
    void InitServer()
    {
        // 忽略SIGPIPE信号
        signal(SIGPIPE, SIG_IGN);
        TcpServer *tsvr = TcpServer::GetInstance(_port);
        // 获取监听描述符
        _lfd = tsvr->GetListenSock();

        // 创建epoll模型
        _epfd = epoll_create(256);
        if (_epfd < 0)
        {
            LOG(FATAL) << "epoll_create fail: create epfd\n";
            exit(1);
        }
        // 监听lfd
        struct epoll_event event;
        event.data.fd = _lfd;
        event.events = EPOLLIN | EPOLLET; // 监听读事件

        if (epoll_ctl(_epfd, EPOLL_CTL_ADD, _lfd, &event) < 0)
        {
            LOG(FATAL) << "epoll_ctl fail: add lfd\n";
            exit(2);
        }
        // LOG(INFO) << "fd: " << _lfd << " epfd: " << _epfd << std::endl;
    }
    void Start()
    {
        LOG(INFO) << "server start\n";
        // LOG(INFO) << "fd: " << _lfd << " epfd: " << _epfd << std::endl;
        //  接受触发事件缓冲区
        struct epoll_event events[TRIGGER_EVENTS_BUFFER_SIZE];
        while (true)
        {
            int ret = epoll_wait(_epfd, events, sizeof(events) / sizeof(struct epoll_event), -1);
            if (ret < 0)
            {
                LOG(WARNING) << " epoll_wait fail\n";
                continue;
            }
            // LOG(INFO) << "ret: " << ret << " epfd: " << _epfd << std::endl;
            //  遍历触发文件描述符
            for (int i = 0; i < ret; ++i)
            {
                int fd = events[i].data.fd;
                // printf("%d\n", fd);
                if (fd == _lfd)
                {
                    // 获取新连接
                    AcceptClient();
                }
                else
                {
                    // 有通信读事件
                    Http http(fd);
                    http.RecvHttpRequest(_epfd);
                }
            }
        }
    }

private:
    void Start()
    {
        LOG(INFO) << "server start\n";
        // LOG(INFO) << "fd: " << _lfd << " epfd: " << _epfd << std::endl;
        //  接受触发事件缓冲区
        struct epoll_event events[TRIGGER_EVENTS_BUFFER_SIZE];
        while (true)
        {
            int ret = epoll_wait(_epfd, events, sizeof(events) / sizeof(struct epoll_event), -1);
            if (ret < 0)
            {
                LOG(WARNING) << " epoll_wait fail\n";
                continue;
            }
            // LOG(INFO) << "ret: " << ret << " epfd: " << _epfd << std::endl;
            //  遍历触发文件描述符
            for (int i = 0; i < ret; ++i)
            {
                int fd = events[i].data.fd;
                // printf("%d\n", fd);
                if (fd == _lfd)
                {
                    // 获取新连接
                    AcceptClient();
                }
                else
                {
                    // 有通信读事件
                    Http http(fd);
                    http.RecvHttpRequest(_epfd);
                }
            }
        }
    }
    void AcceptClient()
    {
        //  获取新连接并放入epoll模型中
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int newFd = accept(_lfd, (sockaddr *)&peer, &len);
        if (newFd < 0)
        {
            LOG(WARNING) << "accept newfd fail\n";
            return;
        }

        // 将新连接设置为非阻塞并添加到epoll模型中
        SetNoBlock(newFd);
        struct epoll_event event;
        event.data.fd = newFd;
        event.events = EPOLLIN | EPOLLET;

        if (epoll_ctl(_epfd, EPOLL_CTL_ADD, newFd, &event) < 0)
        {
            LOG(WARINIG) << "epoll_ctl fail: add newfd\n";
            return;
        }
    }
    void SetNoBlock(int fd)
    {
        // 获取状态标记
        int flag = fcntl(fd, F_GETFL);
        if (flag < 0)
        {
            LOG(WARNING) << "fcntl fail: F_GETFL\n";
            return;
        }
        // 设置
        fcntl(fd, F_SETFL, flag | O_NONBLOCK);
    }

private:
    int _port;
    int _epfd;
    int _lfd;
    EventLoop *_mainLoop;
    ThreadPool *_tp;
};